//#define DEBUG

#region Using Statements
using System;
using System.Collections.Generic;//using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion



namespace Pauliver
{

    public class GameObject //this should become a Pure Virtual class
    {
        // base scene graph, however if you need more renderable components
        // just create another one and add it to scene graph with non default name
        // and be sure to update them in the 
        // Draw calls ect...
        protected System.Collections.Hashtable _ComponentCollection;
        protected Game1 _GameInstance;
        protected EventManager _EventManager;

        public GameObject()
        {
            _GameInstance = Game1.Instance;
            _ComponentCollection = new System.Collections.Hashtable();
            _EventManager = EventManager.GetInstance();

            //AddComponent(new DrawLayer(DrawLayer.LayerDepth.ObjectLayer));
            AddComponent(new SoundEffect());
        }
        public virtual void RemoveComponent(ComponentType p_ComponentType)
        {
            (_ComponentCollection[p_ComponentType] as Component).Parent = null;
            //_ComponentCollection[p_ComponentType] = null;
            _ComponentCollection.Remove(p_ComponentType);
        }

        //these should be abstract when this class becomes a PureVirtual class
        public virtual void Update(GameTime p_time)
        {
            foreach (Component gameComponent in _ComponentCollection.Values)
            {
                gameComponent.Update(p_time);
            }
        }

        public virtual void Draw(SpriteBatch p_SpriteBatch, GameTime p_GameTime, Rectangle p_Screen)
        {
            foreach (Component GameComponent in _ComponentCollection.Values)
            {
                if (GameComponent is DrawableComponent)
                {
                    (GameComponent as DrawableComponent).Draw(p_SpriteBatch, p_GameTime, p_Screen);
                }
            }
        }

        public virtual void ProcessInput(PlayerIndex p_Index)
        {
            //Should Use PlayerStage at some point
            
            foreach (Component c in _ComponentCollection.Values)
            {
                if (c is Input)
                {
                    (c as Input).ProcessInput(p_Index);
                }
                if (c is Holder_Projectile)
                {
                    (c as Holder_Projectile).Projectile.ProcessInput(p_Index);
                }
                if (c is Holder_Weapon)
                {
                    (c as Holder_Weapon).Weapon.ProcessInput(p_Index);
                }
            }
            /*foreach (Component c in _ComponentCollection.Values)
            {
            }
            foreach (Component c in _ComponentCollection.Values)
            {
            }*/
            //@@ Can we combine all of these updates?
        }

        public virtual void AddComponent(Component p_Component)
        {
            this[p_Component.Type] = p_Component;
        }
        public virtual void AddComponent(ComponentType p_ComponentName, Component p_Component)
        {
            this[p_ComponentName] = p_Component;
        }

        public virtual void DeleteMe()
        {
            //we need to figure out exactly how object remove occurs
            //since the EventManager update occures after the GameLevel Update
            //it would remove this object outside of any foreach loop
            //which is what we are trying to avoid
            this._GameInstance.GameManager.RemoveFromSceneGraph(this);
        }

        public virtual Component this[ComponentType p_ComponentName]
        {
            set
            {
                (value as Component).Parent = this;
                (value as Component).Init();
                _ComponentCollection[p_ComponentName] = value;
            }
            get
            {
                //if (_ComponentCollection.Contains(p_ComponentName))
                //{
                    return _ComponentCollection[p_ComponentName] as Component;
                //}
                //else
                //{
                //    return null;
                //}
            }
        }

        public virtual bool GetComponent<T>(ComponentType type, out T Object)
        {
            Object = (T)_ComponentCollection[type];
            if (Object == null)
            {
                return false;
            }
            return true;
        }

        public virtual T GetComponent<T>(ComponentType type, out bool IsIn)
        {
            IsIn = false;
            T Object = (T)_ComponentCollection[type];
            if (Object != null)
            {
                IsIn = true;
            }
            return Object;
        }
    }
}